#include"common.hpp"
#include"Tool.hpp"
#include<pthread.h>
#include<algorithm>
#include <boost/algorithm/string/replace.hpp>

namespace ns_index{

const std::string source = "./data/source/source.txt";

    struct Docinfo{
        std::string _title;
        std::string _content;
        std::string _url;
        uint64_t _doc_id;
    };

    struct InvertedElem{
        uint64_t _doc_id;
        int _weight;
        std::string _keyword;
    };

    typedef std::vector<InvertedElem> Invertlist;//Elem
    class index{

        private:
            static index* _instance;
            static pthread_mutex_t _mutex;
            std::vector<Docinfo> _forward_index;
            std::unordered_map<std::string,Invertlist> _invert_index;

            index(){}
            ~index(){}
        public:
            index(const index& ) = delete;
            index(const index&& ) = delete;

            index& operator=(const index&) = delete;
        public:
            Docinfo* GetForwarddoc(const uint64_t& doc_id){
                if(doc_id >= _forward_index.size()) {
                    std::cerr<< "No find doc"<<std::endl;
                    return nullptr;
                }
                return &_forward_index[doc_id];
            }

            Invertlist* GetInvertdoc(const std::string& keyword){
                auto iter = _invert_index.find(keyword);
                if(iter == _invert_index.end())
                    return nullptr;
                return &_invert_index[keyword];
            }   

            bool Buideindex(const std::string& filepath = source){
                Buideforwardindex(filepath);
                Buideinvertindex();
                return true;
            }

            static index* GetIntance(){
                if(_instance == nullptr){
                    pthread_mutex_lock(&_mutex);
                    if(_instance == nullptr){
                        _instance = new index();
                    }
                    pthread_mutex_unlock(&_mutex);
                }
                return _instance;
            }

        private:
            bool Buideforwardindex(const std::string& filepath){
            #define SEP "\3"
                std::string sep = SEP;
                std::ifstream ifs(filepath,std::ios::in);
                if(!ifs.is_open()){
                    std::cerr<<"ifstream open false"<<std::endl;
                    return false;
                }
                std::string line;
                std::vector<std::string> onehtml;
                Docinfo doc;
                while(getline(ifs,line)){
                    boost::algorithm::replace_all(line, "\xC2\xA0", " ");
                    if(!Tool::BoostSplit::Splithtml(line,&onehtml,sep)){
                        std::cerr<<"Curhtml one false"<<std::endl;
                        continue;
                    }
                    doc._title = onehtml[0];
                    doc._content = onehtml[1];
                    doc._url = onehtml[2];
                    doc._doc_id = _forward_index.size();

                    _forward_index.emplace_back(std::move(doc));
                }

                ifs.close();
                return true;
            }

            bool Buideinvertindex(){

                for(auto& doc: _forward_index){
                    struct word_cnt{
                        uint64_t title_cnt;
                        uint64_t content_cnt;
                    };
                    std::vector<std::string> keywords;
                    std::unordered_map<std::string,word_cnt> wordcnt;
                    //title
                    Tool::Jiebasplit::Splitkeyword(doc._title,&keywords);
                    for(auto& word :keywords){
                        boost::to_lower(word);
                        wordcnt[word].title_cnt++;
                    }
                    //content
                    Tool::Jiebasplit::Splitkeyword(doc._content,&keywords);
                    for(auto& word :keywords){
                        boost::to_lower(word);
                        wordcnt[word].content_cnt++;
                    }

                    for(auto& word :wordcnt){
                        std::string keyword = word.first;
                        boost::to_lower(keyword);
                        InvertedElem invelem;
                        invelem._doc_id = doc._doc_id;
                        invelem._keyword = keyword;
                        
                        invelem._weight = 10*(word.second.title_cnt)+word.second.content_cnt;
                        Invertlist& list = _invert_index[word.first];
                        list.emplace_back(std::move(invelem));
                    }

                }
                return true;
            }
    };

    index* index::_instance = nullptr;
    pthread_mutex_t index::_mutex = PTHREAD_MUTEX_INITIALIZER;
}
